package cn.nju.seg.atg.model;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;

/**
 * 条件表达式
 * @author ChengXin
 *
 */
@SuppressWarnings("deprecation")
public class Constraint {
	/**
	 * 约束条件表达式
	 */
	private IASTExpression expression;
	
	/**
	 * 原子约束组的集合
	 */
	private List<List<String>> atomicConstraintGroups;
	
	/**
	 * 约束组中原子约束的个数
	 */
	private int numOfAtomicConstraint;
	
	/**
	 * 约束组中含“==”的原子约束的个数
	 */
	private int numOfEqualSymbol;
	
	/**
	 * 约束组中含"!="的原子约束的个数
	 */
	private int numOfNotEqualSymbol;
	
	/**
	 * 带参构造函数
	 * @param expression
	 */
	public Constraint(IASTExpression expression)
	{
		this.expression = expression;
		this.atomicConstraintGroups = new ArrayList<List<String>>();
		this.numOfAtomicConstraint = 0;
		this.numOfEqualSymbol = 0;
		this.numOfNotEqualSymbol = 0;
		this.getAtomicConstraintGroupsFromExpression();
	}
	
	/**
	 * 获取expression的等价原子约束组的集合
	 */
	private void getAtomicConstraintGroupsFromExpression()
	{
		IASTBinaryExpression iabe = removeBrackets(this.expression);
		
		if (isAtomicConstraint(iabe))
		{
			List<String> temp = new ArrayList<String>();
			temp.add(getExpressionStrWithNoSpace(iabe));
			this.atomicConstraintGroups.add(temp);
			
			this.numOfAtomicConstraint = 1;
			if(iabe.getOperator() == IASTBinaryExpression.op_equals)
			{
			    this.numOfEqualSymbol = 1;
			}
			else if(iabe.getOperator() == IASTBinaryExpression.op_notequals){
				this.numOfNotEqualSymbol = 1;
			}
		}
		else
		{
			List<String> temp = new ArrayList<String>();
			this.atomicConstraintGroups.add(temp);
			
			int[] beginIndex = new int[1];
			int[] endIndex = new int[1];
			beginIndex[0] = 0;
			endIndex[0] = 1;
			
			String operator = ASTSignatureUtil.getBinaryOperatorString(iabe);
			if (operator.equals("&&"))
			{
				generateAtomicConstraintGroupsForAND(beginIndex[0], endIndex, iabe);
			}
			else if (operator.equals("||"))
			{
				this.partCopy(beginIndex[0], endIndex[0]);
				generateAtomicConstraintGroupsForOR(beginIndex, endIndex, iabe);
			}
		}
	}
	
	/**
	 * 递归函数
	 * 用于为&&复合条件生成原子约束组的集合
	 * @param beginIndex
	 * @param endIndex
	 * @param e
	 */
	private void generateAtomicConstraintGroupsForAND(int beginIndex, int[] endIndex, IASTBinaryExpression iabe)
	{
		//处理左节点
		IASTBinaryExpression iabeLeft = removeBrackets(iabe.getOperand1());
		if (isAtomicConstraint(iabeLeft))
		{
			for (int i=beginIndex; i<endIndex[0]; i++)
			{
				this.numOfAtomicConstraint ++;
				if(iabeLeft.getOperator() == IASTBinaryExpression.op_equals)
				{
				    this.numOfEqualSymbol ++;
				} 
				else if(iabeLeft.getOperator() == IASTBinaryExpression.op_notequals){
					this.numOfNotEqualSymbol ++;
				}
				this.atomicConstraintGroups.get(i).add(getExpressionStrWithNoSpace(iabeLeft));
			}
		}
		else
		{
			int[] beginIndexTemp = new int[1];
			beginIndexTemp[0] = beginIndex;
			
			String operatorLeft = ASTSignatureUtil.getBinaryOperatorString(iabeLeft);
			if (operatorLeft.equals("&&"))
			{
				generateAtomicConstraintGroupsForAND(beginIndexTemp[0], endIndex, iabeLeft);
			}
			else if(operatorLeft.equals("||"))
			{
				this.partCopy(beginIndexTemp[0], endIndex[0]);
				generateAtomicConstraintGroupsForOR(beginIndexTemp, endIndex, iabeLeft);
			}
		}
		//处理右节点
		IASTBinaryExpression iabeRight = removeBrackets(iabe.getOperand2());
		if (isAtomicConstraint(iabeRight))
		{
			for (int i=beginIndex; i<endIndex[0]; i++)
			{
				this.numOfAtomicConstraint ++;
				if(iabeRight.getOperator() == IASTBinaryExpression.op_equals)
				{
				    this.numOfEqualSymbol ++;
				} 
				else if(iabeRight.getOperator() == IASTBinaryExpression.op_notequals){
					this.numOfNotEqualSymbol ++;
				}
				this.atomicConstraintGroups.get(i).add(getExpressionStrWithNoSpace(iabeRight));
			}
		}
		else
		{
			int[] beginIndexTemp = new int[1];
			beginIndexTemp[0] = beginIndex;
			
			String operatorRight = ASTSignatureUtil.getBinaryOperatorString(iabeRight);
			if (operatorRight.equals("&&"))
			{
				generateAtomicConstraintGroupsForAND(beginIndexTemp[0], endIndex, iabeRight);
			}
			else if (operatorRight.equals("||"))
			{
				this.partCopy(beginIndexTemp[0], endIndex[0]);
				generateAtomicConstraintGroupsForOR(beginIndexTemp, endIndex, iabeRight);
			}
		}
	}
	
	/**
	 * 递归函数
	 * 用于为||复合条件生成原子约束条件组的集合
	 * @param beginIndex
	 * @param endIndex
	 * @param e
	 */
	private void generateAtomicConstraintGroupsForOR(int[] beginIndex, int[] endIndex, IASTBinaryExpression iabe)
	{
		//计算当前被复制的原子约束条件分组的个数
		int temp = endIndex[0] - beginIndex[0];
		
		//处理左节点
		IASTBinaryExpression iabeLeft = removeBrackets(iabe.getOperand1());
		if (isAtomicConstraint(iabeLeft))
		{
			for (int i=beginIndex[0]; i<endIndex[0]; i++)
			{
				this.numOfAtomicConstraint ++;
				if(iabeLeft.getOperator() == IASTBinaryExpression.op_equals)
				{
				    this.numOfEqualSymbol ++;
				} 
				else if(iabeLeft.getOperator() == IASTBinaryExpression.op_notequals){
					this.numOfNotEqualSymbol ++;
				}
				this.atomicConstraintGroups.get(i).add(getExpressionStrWithNoSpace(iabeLeft));
			}
		}
		else
		{
			int[] beginIndexTemp = new int[1];
			beginIndexTemp[0] = beginIndex[0];
			
			String operatorLeft = ASTSignatureUtil.getBinaryOperatorString(iabeLeft);
			if (operatorLeft.equals("&&"))
			{
				generateAtomicConstraintGroupsForAND(beginIndexTemp[0], endIndex, iabeLeft);
			}
			else if (operatorLeft.equals("||"))
			{
				this.partCopy(beginIndexTemp[0], endIndex[0]);
				generateAtomicConstraintGroupsForOR(beginIndexTemp, endIndex, iabeLeft);
			}
		}
		//处理游标
		beginIndex[0] = endIndex[0];
		endIndex[0] = endIndex[0] + temp;
		//处理右节点
		IASTBinaryExpression iabeRight = removeBrackets(iabe.getOperand2());
		if (isAtomicConstraint(iabeRight))
		{
			for (int i=beginIndex[0]; i<endIndex[0]; i++)
			{
				this.numOfAtomicConstraint ++;
				if(iabeRight.getOperator() == IASTBinaryExpression.op_equals)
				{
				    this.numOfEqualSymbol ++;
				} 
				else if(iabeRight.getOperator() == IASTBinaryExpression.op_notequals){
					this.numOfNotEqualSymbol ++;
				}
				this.atomicConstraintGroups.get(i).add(getExpressionStrWithNoSpace(iabeRight));
			}
		}
		else
		{
			int[] beginIndexTemp = new int[1];
			beginIndexTemp[0] = beginIndex[0];
			
			String operatorRight = ASTSignatureUtil.getBinaryOperatorString(iabeRight);
			if (operatorRight.equals("&&"))
			{
				generateAtomicConstraintGroupsForAND(beginIndexTemp[0], endIndex, iabeRight);
			}
			else if (operatorRight.equals("||"))
			{
				this.partCopy(beginIndexTemp[0], endIndex[0]);
				generateAtomicConstraintGroupsForOR(beginIndexTemp, endIndex, iabeRight);
			}
		}
	}
	
	/**
	 * 拷贝atomicConstraintGroups中的[beginIndex, endIndex-1]部分
	 * 并将其插入到(endIndex-1)与endIndex之间
	 * @param beginIndex
	 * @param endIndex
	 */
	private void partCopy(int beginIndex, int endIndex)
	{
		int atomicConstraintGroupsSize = this.atomicConstraintGroups.size();
		List<List<String>> headListTemp = new ArrayList<List<String>>();
		List<List<String>> tailListTemp = new ArrayList<List<String>>();
		
		for (int i=0; i<endIndex; i++)
		{
			List<String> temp = new ArrayList<String>();
			int sizeTemp = this.atomicConstraintGroups.get(i).size();
			for (int j=0; j<sizeTemp; j++)
			{
				temp.add(this.atomicConstraintGroups.get(i).get(j));
			}
			headListTemp.add(temp);
		}
		for (int i=endIndex; i<atomicConstraintGroupsSize; i++)
		{
			List<String> temp = new ArrayList<String>();
			int sizeTemp = this.atomicConstraintGroups.get(i).size();
			for (int j=0; j<sizeTemp; j++)
			{
				temp.add(this.atomicConstraintGroups.get(i).get(j));
			}
			tailListTemp.add(temp);
		}
		
		for (int i=beginIndex; i<endIndex; i++)
		{
			List<String> temp = new ArrayList<String>();
			int sizeTemp = this.atomicConstraintGroups.get(i).size();
			for (int j=0; j<sizeTemp; j++)
			{
				temp.add(this.atomicConstraintGroups.get(i).get(j));
			}
			headListTemp.add(temp);
		}
		int tailListTempSize = tailListTemp.size();
		for (int i=0; i<tailListTempSize; i++)
		{
			List<String> temp = new ArrayList<String>();
			int sizeTemp = tailListTemp.get(i).size();
			for (int j=0; j<sizeTemp; j++)
			{
				temp.add(tailListTemp.get(i).get(j));
			}
			headListTemp.add(temp);
		}
		this.atomicConstraintGroups = headListTemp;
	}
	
	/**
	 * 去除约束条件的外层括号
	 * @param iae
	 * @return 无外层括号的约束条件
	 */
	private static IASTBinaryExpression removeBrackets(IASTExpression iae)
	{
		while(iae instanceof IASTUnaryExpression)
		{
			iae = (IASTExpression) iae.getChildren()[0];
		}
		return (IASTBinaryExpression)iae;
	}
	
	/**
	 * 判断一个约束条件是否为原子约束条件
	 * @param iabe
	 * @return true|false
	 */
	private static boolean isAtomicConstraint(IASTBinaryExpression iabe)
	{
		String operator = ASTSignatureUtil.getBinaryOperatorString(iabe);
		if (operator.equals("&&") || operator.equals("||"))
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	
	/**
	 * 获取一个原子约束条件的表达式字符串
	 * 去除字符串中的空格/制表符
	 * @param iabe
	 * @return
	 */
	private static String getExpressionStrWithNoSpace(IASTBinaryExpression iabe)
	{
		return removeGap(iabe.getRawSignature());
	}
	
	/**
	 * 去掉字符串中的空格、回车、换行符、制表符
	 * @param str
	 * @return 去掉后的字符串
	 */
	public static String removeGap(String str)
	{
		Pattern p = Pattern.compile("\\s*|\t|\r|\n");
		Matcher m = p.matcher(str);
		return m.replaceAll("");
	}

	public int getNumOfAtomicConstraint(){
		return numOfAtomicConstraint;
	}
	
	public int getNumOfEqualSymbol(){
		return numOfEqualSymbol;
	}
	
	public int getNumOfNotEqualSymbol(){
	    return numOfNotEqualSymbol;
	}
	
	public IASTExpression getExpression() {
		return expression;
	}

	public void setExpression(IASTExpression expression) {
		this.expression = expression;
	}

	public List<List<String>> getAtomicConstraintGroups() {
		return atomicConstraintGroups;
	}

	public void setAtomicConstraintGroups(List<List<String>> atomicConstraintGroups) {
		this.atomicConstraintGroups = atomicConstraintGroups;
	}
}
